#ifndef DYNAMIC_PROPERTY_IMPL_H
#define DYNAMIC_PROPERTY_IMPL_H
#include "type_helper.h"
#include <map>
#include <vector>
#include <iostream>
#include <boost/type_traits/is_integral.hpp>
#include <boost/limits.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/ref.hpp>
#include <limits>

namespace vpm 
{
	namespace utils
	{
		namespace detail
		{
			typedef boost::function<void()> ValueOperator; 

			template <class KeyT,class INTEGRAL_KEY = KeyTraitType<boost::is_integral<KeyT>::value >  > 
			class DynamicPropertyImpl
			{
			public:
				typedef std::pair<boost::any,ValueOperator>  PropertyPair;
				typedef std::vector<std::pair<boost::any,ValueOperator> > Properties; 
				typedef std::vector<KeyT> MandatoryProperties;

#ifdef WIN32
				DynamicPropertyImpl(KeyT maxIndex =512):m_properties(maxIndex)
#else 
				DynamicPropertyImpl(KeyT maxIndex =
					(std::numeric_limits<KeyT>::max()< 512?std::numeric_limits<KeyT>::max():512))
					:m_properties(maxIndex) //TODO when key type is unsigned char , 
					//maybe it's a bug, so we need a template method to get the max of this type
#endif //
				{

				}

				template<class ValueT>
				void property_value_operator(const KeyT &k,boost::function<void(ValueT)> op)
				{
					op(TypeGetter<ValueT>()(m_properties[k].first));
				}

				template <class ValueT> 
				bool set_property_value( const KeyT &k , ValueT  v, boost::function<void(ValueT)> op)
				{
					if ((Properties::size_type ) k >= m_properties.size() )
					{
						printf("out of range , max index is %d\n",m_properties.size()-1);
						return false;
					}

					if ( !m_properties[k].empty() )
					{
						//check the type;
						if (m_properties[k].type() != typeid(v))
						{
							std::cout<< "Can't update a property with another type value!\n";
							return false;
						}
					}
					op(v);
					m_properties[k].first = TypeSetter<ValueT>()(v);
					m_properties[k].second = boost::bind(&DynamicPropertyImpl::template property_value_operator<ValueT>,this,k,op);
					//if use this way, the operator will get original value 
					//m_properties[k].second = boost::bind(op,boost::ref(TypeGetter<ValueT>()(m_properties[k].first)));
					return true; 
				}

				template<class ValueT>
				void property_operator(const KeyT &k,boost::function<void(const KeyT&,ValueT)> op)
				{
					op(k,TypeGetter<ValueT>()(m_properties[k].first));
				}

				template <class ValueT> 
				bool set_property( const KeyT &k , ValueT  v, boost::function<void(const KeyT&,ValueT)> op)
				{
					if ((Properties::size_type ) k >= m_properties.size() )
					{
						printf("out of range , max index is %d\n",m_properties.size()-1);
						return false;
					}

					if ( !m_properties[k].empty() )
					{
						//check the type;
						if (m_properties[k].type() != typeid(v))
						{
							std::cout<< "Can't update a property with another type value!\n";
							return false;
						}
					}
					op(v);
					m_properties[k].first = TypeSetter<ValueT>()(v);
					m_properties[k].second = boost::bind(&DynamicPropertyImpl::template property_operator<ValueT>,this,k,op);
					//if use this way, the operator will get original value 
					//m_properties[k].second = boost::bind(op,boost::ref(TypeGetter<ValueT>()(m_properties[k].first)));
					return true; 
				}

				template <class ValueT> 
				bool set_property( const KeyT &k , ValueT  v)
				{
					if ((Properties::size_type ) k >= m_properties.size() )
					{
						printf("out of range , max index is %d\n",m_properties.size()-1);
						return false;
					}

					if ( !m_properties[k].first.empty() )
					{
						//check the type;
						if (m_properties[k].first.type() != typeid(v))
						{
							std::cout<< "Can't update a property with another type value!\n";
							return false;
						}
						m_properties[k].first = v; 
					}
					else 
					{
						m_properties[k].first = TypeSetter<ValueT>()(v);		
					}

					if (m_properties[k].second)
					{
						m_properties[k].second(); 		
					}
					return true; 
				}


				template <class ValueT> 
				ValueT get_property(const KeyT  &k)
				{
					if ((Properties::size_type ) k < m_properties.size() && !m_properties[k].first.empty())
					{					
						return TypeGetter<ValueT>()(m_properties[k].first);
					}
					return ValueT(); 
				}

				void set_mandatory_property(const KeyT k)
				{
					m_mandatoryProperties.push_back(k);
				}

				bool check_mandatory_properties()
				{
					bool result = false; 
					for(typename MandatoryProperties::iterator itr = m_mandatoryProperties.begin(); itr != m_mandatoryProperties.end(); ++itr)
					{
						if (m_properties[*itr].first.empty())
						{
							std::cout << "Property [" <<*itr<<"] not set."<<std::endl;
						}
					}
					return result;
				}

			private:
				Properties m_properties; 
				MandatoryProperties m_mandatoryProperties;
			};

			template<class KeyT >
			class DynamicPropertyImpl<KeyT ,STRING_KEY >
			{
			public:
				DynamicPropertyImpl()
				{

				}

				typedef std::pair<boost::any,ValueOperator>  PropertyPair;
				typedef std::map<KeyT,std::pair<boost::any,ValueOperator> > Properties; 
				typedef std::vector<KeyT> MandatoryProperties;


				template<class ValueT>
				void property_value_operator(const KeyT &k,boost::function<void(ValueT)> op)
				{
					op(TypeGetter<ValueT>()(m_properties[k].first));
				}


				template <class ValueT> 
				bool set_property_value( const KeyT & k , ValueT  v, boost::function<void(ValueT)>  op)
				{
					typename Properties::iterator itr = m_properties.find(k);
					if (itr != m_properties.end())
					{
						//check the type;
						if (itr->second.first.type() != typeid(v))
						{
							std::cout<<"Can't update a string property with another type value!\n";
							return false;
						}
					}
					op(v);
					m_properties[k].first = TypeSetter<ValueT>()(v);
					m_properties[k].second = boost::bind(&DynamicPropertyImpl::template property_value_operator<ValueT>,this,k,op);
					return true; 
				}

				template<class ValueT>
				void property_operator(const KeyT & k,boost::function<void(const KeyT &,ValueT)> op)
				{
					op(k,TypeGetter<ValueT>()(m_properties[k].first));
				}

				template <class ValueT> 
				bool set_property( const KeyT & k , ValueT  v, boost::function<void(const KeyT&,ValueT)>  op)
				{
					typename Properties::iterator itr = m_properties.find(k);
					if (itr != m_properties.end())
					{
						//check the type;
						if (itr->second.first.type() != typeid(v))
						{
							std::cout<<"Can't update a string property with another type value!\n";
							return false;
						}
					}
					op(k,v);
					m_properties[k].first = TypeSetter<ValueT>()(v);
					m_properties[k].second = boost::bind(&DynamicPropertyImpl::template property_operator<ValueT>,this,k,op);
					return true; 
				}


				template <class ValueT> 
				bool set_property( const KeyT & k , ValueT  v)
				{
					typename Properties::iterator itr = m_properties.find(k);
					if (itr != m_properties.end())
					{
						//check the type;
						if (itr->second.first.type() != typeid(v))
						{
							std::cout<<"Can't update a string property with another type value!\n";
							return false;
						}
						m_properties[k].first = TypeSetter<ValueT>()(v);
					}
					else 
					{
						m_properties[k].first = TypeSetter<ValueT>()(v);
					}

					if (m_properties[k].second) 
					{
						m_properties[k].second();
					}

					return true; 
				}

				template <class ValueT> 
				ValueT get_property(const KeyT & k)
				{
					typename Properties::iterator itr = m_properties.find(k);

					if (itr != m_properties.end())
					{					
						return TypeGetter<ValueT>()(itr->second.first);
					}
					return ValueT(); 
				}

				void set_mandatory_properties(const KeyT &k)
				{
					m_mandatoryProperties.push_back(k);
				}

				bool check_mandatory()
				{
					bool result = false; 
					for(typename MandatoryProperties::iterator itr = m_mandatoryProperties.begin(); itr != m_mandatoryProperties.end(); ++itr)
					{
						if (m_properties[*itr].empty())
						{
							std::cout << "Property [" <<*itr<<"] not set."<<std::endl;
						}
					}
					return result;
				}
			private:
				Properties m_properties; 
				MandatoryProperties m_mandatoryProperties;
			};

		}
	}
}
#endif // 
